﻿using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.AspNetCore.Identity;

namespace Devonline.Identity;

/// <summary>
/// 用户 字符串类型的默认实现
/// </summary>
[Table("user"), DisplayName("用户")]
public class User : User<string>, IIdentity, IEntitySet, IEntitySetWithCreate, IEntitySetWithCreateAndUpdate
{
    /// <summary>
    /// 用户级别
    /// </summary>
    public virtual Level Level { get; set; }
    /// <summary>
    /// 通用附件集合, NotMapped Attachments 用于记录实体对象上上传的附件
    /// </summary>
    [NotMapped]
    public virtual ICollection<Attachment> Attachments { get; set; }
}

/// <summary>
/// 用户
/// 用户和个人信息是一对一关系, 没有字段相关指向, 也没有外键直接关联, 他们的主键是同一个值
/// 用户和认证信息是一对一关系, 没有字段相关指向, 也没有外键直接关联, 他们的主键是同一个值
/// </summary>
[Table("user"), DisplayName("用户")]
public class User<TKey> : IdentityUser<TKey>, IIdentity<TKey>, IEntitySet<TKey>, IEntitySetWithCreate<TKey>, IEntitySetWithCreateAndUpdate<TKey> where TKey : IEquatable<TKey>, IConvertible
{
    public User()
    {
        Id = KeyGenerator.GetKey<TKey>();
        RowVersion = Id;
        SecurityStamp = Id.ToString();
    }

    #region base fields from IEntitySetWithCreateAndUpdate<TKey>
    /// <summary>
    /// 数据主键
    /// </summary>
    [Column("id"), DisplayName("编号"), DatabaseGenerated(DatabaseGeneratedOption.None), Key, MaxLength(36), PersonalData, Excel]
    public override TKey Id { get; set; }
    /// <summary>
    /// 行版本号
    /// </summary>
    [Column("row_version"), DisplayName("行版本号"), ConcurrencyCheck]
    public virtual TKey RowVersion { get; set; }
    /// <summary>
    /// 数据状态
    /// </summary>
    [Column("state", TypeName = "VARCHAR(16)"), DisplayName("数据状态"), DefaultValue(DataState.Available), Excel]
    public virtual DataState State { get; set; }
    /// <summary>
    /// 创建时间
    /// </summary>
    [Column("created_on"), DisplayName("创建时间"), Excel]
    public virtual DateTime? CreatedOn { get; set; }
    /// <summary>
    /// 创建人
    /// </summary>
    [Column("created_by"), DisplayName("创建人"), MaxLength(36), Excel]
    public virtual string CreatedBy { get; set; }
    /// <summary>
    /// 更新时间
    /// </summary>
    [Column("updated_on"), DisplayName("更新时间"), Excel]
    public virtual DateTime? UpdatedOn { get; set; }
    /// <summary>
    /// 更新人
    /// </summary>
    [Column("updated_by"), DisplayName("更新人"), MaxLength(36), Excel]
    public virtual string UpdatedBy { get; set; }
    /// <summary>
    /// 备注说明
    /// </summary>
    [Column("description"), DisplayName("备注说明"), MaxLength(256), Excel]
    public virtual string Description { get; set; }
    #endregion

    #region self fields
    /// <summary>
    /// 名称
    /// </summary>
    [ProtectedPersonalData]
    [Column("name"), Required, MaxLength(256), DisplayName("名称"), Excel]
    public virtual string Name { get; set; }
    /// <summary>
    /// 昵称
    /// </summary>
    [ProtectedPersonalData]
    [Column("alias"), MaxLength(256), DisplayName("昵称"), Excel]
    public virtual string Alias { get; set; }
    /// <summary>
    /// 头像
    /// </summary>
    [Column("image"), MaxLength(128), DisplayName("头像"), BusinessType(IsAttachment = true), Excel]
    public virtual string Image { get; set; }
    /// <summary>
    /// 授权类型
    /// </summary>
    [Column("type", TypeName = "VARCHAR(16)"), DisplayName("授权类型"), DefaultValue(AuthorizeType.Internal), Excel]
    public virtual AuthorizeType Type { get; set; }
    /// <summary>
    /// 用户级别
    /// </summary>
    [Column("level_id"), DisplayName("用户级别编号"), MaxLength(36), Excel]
    public virtual TKey LevelId { get; set; }
    #endregion

    #region override base IdentityUser fields
    //
    // 摘要:
    //     Gets or sets the date and time, in UTC, when any user lockout ends.
    //
    // 言论：
    //     A value in the past means the user is not locked out.
    [Column("lockout_end"), DisplayName("锁定结束时间"), Excel]
    public override DateTimeOffset? LockoutEnd { get; set; }
    //
    // 摘要:
    //     Gets or sets a flag indicating if two factor authentication is enabled for this
    //     user.
    //
    // 值:
    //     True if 2fa is enabled, otherwise false.
    [PersonalData]
    [Column("two_factor_enabled"), DisplayName("双因素确认"), Excel]
    public override bool TwoFactorEnabled { get; set; }
    //
    // 摘要:
    //     Gets or sets a flag indicating if a user has confirmed their telephone address.
    //
    // 值:
    //     True if the telephone number has been confirmed, otherwise false.
    [PersonalData]
    [Column("phone_number_confirmed"), DisplayName("手机号码是否已确认"), Excel]
    public override bool PhoneNumberConfirmed { get; set; }
    /// <summary>
    /// 长字符串内容为密文, 由用户自己的密钥所加密, 适用于安全要求较高的场景
    /// </summary>
    [ProtectedPersonalData]
    [Column("phone_number"), DisplayName("手机号码"), MaxLength(256), Excel]
    public override string PhoneNumber { get; set; }
    //
    // 摘要:
    //     A random value that must change whenever a user is persisted to the store
    [Column("concurrency_stamp"), DisplayName("并发特征"), MaxLength(36), Excel]
    public override string ConcurrencyStamp { get; set; }
    //
    // 摘要:
    //     A random value that must change whenever a users credentials change (password
    //     changed, login removed)
    [Column("security_stamp"), DisplayName("安全特征"), MaxLength(36), Excel]
    public override string SecurityStamp { get; set; }
    //
    // 摘要:
    //     Gets or sets a salted and hashed representation of the password for this user.
    [Column("password_hash"), DisplayName("密码"), MaxLength(256), Excel]
    public override string PasswordHash { get; set; }
    //
    // 摘要:
    //     Gets or sets a flag indicating if a user has confirmed their email address.
    //
    // 值:
    //     True if the email address has been confirmed, otherwise false.
    [PersonalData]
    [Column("email_confirmed"), DisplayName("邮箱是否已确认"), Excel]
    public override bool EmailConfirmed { get; set; }
    //
    // 摘要:
    //     Gets or sets the normalized email address for this user.
    [ProtectedPersonalData]
    [Column("normalized_email"), DisplayName("归一邮箱"), MaxLength(256), Excel]
    public override string NormalizedEmail { get; set; }
    //
    // 摘要:
    //     Gets or sets the email address for this user.
    [ProtectedPersonalData]
    [Column("email"), DisplayName("邮箱"), MaxLength(256), Excel]
    public override string Email { get; set; }
    //
    // 摘要:
    //     Gets or sets the normalized user name for this user.
    [ProtectedPersonalData]
    [Column("normalized_user_name"), DisplayName("归一用户名"), MaxLength(256), Excel]
    public override string NormalizedUserName { get; set; }
    //
    // 摘要:
    //     Gets or sets the user name for this user.
    [ProtectedPersonalData]
    [Column("user_name"), Required, Unique, DisplayName("用户名"), MaxLength(256), Excel]
    public override string UserName { get; set; }
    //
    // 摘要:
    //     Gets or sets a flag indicating if the user could be locked out.
    //
    // 值:
    //     True if the user could be locked out, otherwise false.
    [Column("lockout_enabled"), DisplayName("是否锁定"), Excel]
    public override bool LockoutEnabled { get; set; }
    //
    // 摘要:
    //     Gets or sets the number of failed login attempts for the current user.
    [Column("access_failed_count"), DisplayName("访问失败次数"), Excel]
    public override int AccessFailedCount { get; set; }
    #endregion

    /// <summary>
    /// 创建方法
    /// </summary>
    public virtual void Create(string createdBy = null, DateTimeKind kind = DateTimeKind.Utc)
    {
        Id ??= KeyGenerator.GetKey<TKey>();
        RowVersion = Id;
        CreatedOn ??= DateTime.SpecifyKind(DateTime.Now, kind);
        CreatedBy ??= createdBy ?? AppSettings.USER_SYSTEM;
        ConcurrencyStamp ??= Id.ToString();
    }

    /// <summary>
    /// 更新方法
    /// </summary>
    public virtual void Update(string updatedBy = null, DateTimeKind kind = DateTimeKind.Utc)
    {
        RowVersion = KeyGenerator.GetKey<TKey>();
        UpdatedOn ??= DateTime.SpecifyKind(DateTime.Now, kind);
        UpdatedBy ??= updatedBy ?? AppSettings.USER_SYSTEM;
        ConcurrencyStamp ??= RowVersion.ToString();
    }
}